1 /*
2  * Collie - An asynchronous event-driven network framework using Dlang development
3  *
4  * Copyright (C) 2015-2017  Shanghai Putao Technology Co., Ltd 
5  *
6  * Developer: putao's Dlang team
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 module collie.codec.http.session.httpdownstreamsession;
12 
13 import std.exception;
14 
15 import collie.codec.http.session.httpsession;
16 import collie.codec.http.headers;
17 import collie.codec.http.codec.wsframe;
18 import collie.codec.http.httpmessage;
19 import collie.codec.http.httptansaction;
20 import collie.codec.http.codec.httpcodec;
21 import std.base64;
22 import std.digest.sha;
23 import collie.codec.http.codec.websocketcodec;
24 
25 final class HTTPDownstreamSession : HTTPSession
26 {
27 	this(HTTPSessionController controller,HTTPCodec codec, SessionDown down)
28 	{
29 		super(controller,codec,down);
30 	}
31 
32 protected:
33 	override void setupOnHeadersComplete(ref HTTPTransaction txn,
34 		HTTPMessage msg)
35 	{
36 		auto handle =  _controller.getRequestHandler(txn,msg);
37 		if(handle is null)
38 		{
39 			try{
40 				enum string _404 = "<h1>Not Found!</h1><p>the http RequestHandle is null!</p>";
41 				import collie.codec.http.headers;
42 				import std.typecons;
43 				import std.conv;
44 				scope HTTPMessage rmsg = new HTTPMessage();
45 				rmsg.statusCode = 404;
46 				rmsg.statusMessage = HTTPMessage.statusText(404);
47 				rmsg.getHeaders.add(HTTPHeaderCode.CONNECTION,"close");
48 				rmsg.getHeaders.add(HTTPHeaderCode.CONTENT_LENGTH,to!string(_404.length));
49 				sendHeaders(txn,rmsg,false);
50 				sendBody(txn,cast(ubyte[])_404,true);
51 				txn = null;
52 			} catch (Exception e){
53 				import collie.utils.exception;
54 				showException(e);
55 			}
56 		} else {
57 			txn.handler(handle);
58 			txn.onIngressHeadersComplete(msg);
59 		}
60 	}
61 
62 	override void setupProtocolUpgrade(ref HTTPTransaction txn,CodecProtocol protocol,string protocolString,HTTPMessage msg) {
63 		void doErro(){
64 			scope HTTPMessage rmsg = new HTTPMessage();
65 			rmsg.statusCode = 400;
66 			rmsg.statusMessage = HTTPMessage.statusText(400);
67 			rmsg.getHeaders.add(HTTPHeaderCode.CONNECTION,"close");
68 			sendHeaders(txn,rmsg,true);
69 		}
70 		auto handle =  _controller.getRequestHandler(txn,msg);
71 		if(handle is null){
72 			collectException( doErro());
73 			return;
74 		}
75 		txn.handler(handle);
76 		if(protocol == CodecProtocol.init || !txn.onUpgtade(protocol, msg))
77 		{
78 			collectException( doErro());
79 			return;
80 		}
81 		bool rv = true;
82 		switch(protocol){
83 			case CodecProtocol.WEBSOCKET :
84 				rv = doUpgradeWebSocket(txn,msg);
85 				break;
86 			default :
87 				rv = false;
88 				break;
89 		}
90 		if(!rv)
91 			doErro();
92 	}
93 
94 	bool doUpgradeWebSocket(ref HTTPTransaction txn,HTTPMessage msg)
95 	{
96 		string key = msg.getHeaders.getSingleOrEmpty(HTTPHeaderCode.SEC_WEBSOCKET_KEY);
97 		string ver = msg.getHeaders.getSingleOrEmpty(HTTPHeaderCode.SEC_WEBSOCKET_VERSION);
98 		if(ver != "13") 
99 			return false;
100 		auto accept = cast(string) Base64.encode(sha1Of(key ~ WebSocketGuid));
101 
102 		scope HTTPMessage rmsg = new HTTPMessage();
103 		rmsg.statusCode = 101;
104 		rmsg.statusMessage = HTTPMessage.statusText(101);
105 		rmsg.getHeaders.add(HTTPHeaderCode.CONNECTION,"keep-alive");
106 		rmsg.getHeaders.add(HTTPHeaderCode.SEC_WEBSOCKET_ACCEPT,accept);
107 		rmsg.getHeaders.add(HTTPHeaderCode.CONNECTION,"Upgrade");
108 		rmsg.getHeaders.add(HTTPHeaderCode.UPGRADE,"websocket");
109 		sendHeaders(txn,rmsg,false);
110 		restCodeC(new WebsocketCodec(TransportDirection.DOWNSTREAM,txn));
111 		return true;
112 	}
113 
114 }
115 
116